/*
 * Copyright (c) 2012 Intelygenz <www.intelygenz.com>
 * All rights reserved.
 * 
 * BSD License
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Description:
 * 
 * This JavaScript library provides different client-side optimization techniques for front construction.
 * It is independent and compatible with any server-side technology: JAVA, PHP, Python, GOOGLE APP ENGINE, .NET...
 * 
 * Dependences:
 * 	jQuery 1.5.1 +
 *	
 * Download:
 * 
 *  Previous version can be found at: https://iris-js.googlecode.com/svn/tags/
 *  e.g.:
 *  	https://iris-js.googlecode.com/svn/tags/0.1.9/iris.js
 * 
 * Creation date: 2012-01-12
 * 
 * [version] date -> authors
 * 		upd|fix|new|dep|rmv - description
 *
 *
 * [0.2.1] 2012-01-26 -> jonas.dacruz@intelygenz.com
 * 		new - iris.screen.Goto -> added setting "replaceInHistory" allow replace the last screen in navigation 
 * 
 * [0.2.0] 2012-01-24 -> jonas.dacruz@intelygenz.com
 * 		new - iris.Include allows load .js files
 * 		upd - The screen methods $, OnAwake and OnSleep have been syntactically protected with a prefix and suffix: eg. "__$__" 
 * 
 * [0.1.9] 2012-01-24 -> jonas.dacruz@intelygenz.com, angel.sanchez@intelygenz.com
 * 		upd - iris.Include load only the specified html or css file
 *		fix - iris.Include error in IE 8 when iterates arguments with for in
 *		new - iris.Import
 *
 * [0.1.8] 2012-01-20 -> jonas.dacruz@intelygenz.com, angel.sanchez@intelygenz.com
 *		fix - error in IE when detect the console debugging
 *
 * [0.1.7] 2012-01-19 -> jonas.dacruz@intelygenz.com, angel.sanchez@intelygenz.com
 *		new - iris.screen.Back, iris.screen.Forward, iris.screen.transition.Settings
 *
 * [0.1.6] 2012-01-12 -> jonas.dacruz@intelygenz.com, angel.sanchez@intelygenz.com
 *		new - public spin-off from Intelygenz internal project kore-js v0.1.5
 *
 * */

 /*****************************************
 * IRIS PACKAGES
 * 
 * 
 */
 
var iris = {
	  out 	 : {}
	, config : {}
	, global : {}
	, local  : {}
	, event  : {}
	, net 	 : {}
	, lang 	 : {}
	, ui 	 : {}
	, util 	 : {}
	, screen : {}
};

/*****************************************
 * CLASSES
 * 
 * 
 */

//////////////////////////// CORE ///////////////////
(function(){
	// private context
	//
	var _Version = "0.2.0";
	var _Name    = "iris";
	var _JqMinVer= 1.5;	// jQuery oldest version required
	
	var _Includes = {};
	
	function _Import (p_classPath) {
		
		var fileUrl = p_classPath.replace(/\./g,"/").toLowerCase();
		
		_Include(fileUrl + ".css");
		_Include(fileUrl + ".html");
	}
	
	function _Include(p_uiFile) {
		var fileUrl = iris.net.BaseUri() + p_uiFile;
		
		iris.out.D("[iris.ui.Include]", fileUrl);
		
		if ( !_Includes.hasOwnProperty(fileUrl) ) {
			_Includes[fileUrl] = true;
		
			if ( p_uiFile.lastIndexOf(".html") > -1 ) {
				$.ajax(
					{ url: fileUrl
					, dataType: "html"
					, async: false
					, cache: false
					, success : 
						function (p_html){
							$("head").append( p_html );
						}
					, error : 
						function (p_err) {
							delete _Includes[fileUrl];
							iris.out.E(p_err.status, "Error loading ui file", htmlUri);
						}
					}
				);
			}
			else if ( p_uiFile.lastIndexOf(".js") > -1 ) {
				$.ajax(
					{ url: fileUrl
					, dataType: "script"
					, async: false
					, cache: false
					, success : 
						function (p_js){ 
							iris.out.D(fileUrl+" is loaded");
						}
					, error : 
						function (p_err) {
							delete _Includes[fileUrl];
							iris.out.E(p_err.status, "Error loading ui file", htmlUri);
						}
					}
				);
			}
			else {
				if (document.createStyleSheet) {
		            document.createStyleSheet(fileUrl);
		        }
		        else {
		            $("head").append( $("<link rel='stylesheet' href='"+fileUrl+"' type='text/css' />") );
		        }
			}
			
		}
	}

	// public interfaces
	//
	iris.Name = function (){
		return _Name + " v" + _Version + " ["+iris.config.Env()+"]";
	};
	
	iris.JqMinVersion = function () { 
		return _JqMinVer; 
	};
	
	iris.Include = function (){
		
		for ( var f=0,F=arguments.length; f<F; f++ ){
			_Include( arguments[f] );
		}
	};
	
	iris.Import = function (){
		iris.out.D("[iris.ui.Import]", arguments);

		for ( var f=0,F=arguments.length; f<F; f++ ){
			_Import( arguments[f] );
		}
	};

})();

//////////////////////////// OUT ///////////////////
(function(){

	var _HasConsole = (window.console && window.console.debug && window.console.warn && window.console.error);

	if ( !_HasConsole && window.console && window.console.log ) {	// TODO: Think about remove it
		window.console.log("advanced console debugging is not supported in this browser");
	}

	// public interfaces
	//
	iris.out.L = function (){
		if ( _HasConsole && window.console.log) {
			window.console.log("["+iris.config.Env()+"]", arguments);
		}
	};
	iris.out.D = function (){
		if(_HasConsole && iris.config.LogOf("debug") ){
			window.console.debug("["+iris.config.Env()+"]", arguments);
		}
	};
	iris.out.W = function (){
		if(_HasConsole && iris.config.LogOf("warning") ){
			window.console.warn("["+iris.config.Env()+"]", arguments);
		}
	};
	iris.out.E = function (){
		if(_HasConsole && iris.config.LogOf("error") ){
			window.console.error("["+iris.config.Env()+"]", arguments);
		}
	};

})();

//////////////////////////// CONFIG ///////////////////
(function(){
	// private context
	//
	var _Data = {}
	,	_Env = null
	,	_Log = {"error":true}
	;

	// public interfaces
	//
	
	iris.config.LoadFrom = function (p_uri) {
		iris.out.D("Loading config file from", p_uri);
		$.ajax(
			{ url: p_uri
			, dataType: "json"
			, async: false
			, cache: true
			, success : 
				function (p_json){
					iris.config.Load(p_json);
					iris.out.D("config file loaded", p_json);
				}
			, error : 
				function (p_err) {
					iris.out.E(p_err.status, msg, p_uri);
				}
			}
		);
	};

	iris.config.Load = function (p_json){
		if ( p_json ) {
			_Data = p_json;

			iris.global.Load( _Data["global"] );

			if ( _Data["log"] ) {
				var logConfig = _Data["log"][iris.config.Env()];
				var logs = logConfig.split(",");
				for ( var logType in logs ) {
					_Log[ $.trim(logs[logType]) ] = true;
				}
			}
			
			iris.local.Load( _Data["local"] );
		}
		return _Data;
	};
	
	iris.config.LogOf = function (p_type){
		return _Log[p_type];
	};

	iris.config.Env = function (p_env) {
		if ( p_env ) {
			_Env = p_env;
		}
		else {
			if ( !_Env ) {
				_Env = _Data["environment-default"];
				for (var p in _Data["environment"] ){
					if ( document.location.href.indexOf( p ) > -1 ) {
						_Env = _Data["environment"][p];
						break;
					}
				}
				if ( !_Env ) {
					_Env = "pro";
				}
			}
			return _Env;
		}
	};

})();

//////////////////////////// GLOBAL ///////////////////
(function(){
	// private context
	//
	var _Global = {};

	// public interfaces
	//
	iris.global.Load = function (p_hash){
		$.extend(_Global, p_hash);
		return _Global;
	};

	iris.global.Data = function (p_label, p_value){
		if ( p_label && p_value ) {
			_Global[p_label] = p_value; 	
		}
		else if ( p_label ) {
			return _Global[p_label];
		}
		else {
			return _Global;
		}
	};

})();

//////////////////////////// LOCAL ///////////////////
(function(){
	// private context
	//
	var _Local = {};

	// public interfaces
	//
	iris.local.Load = function (p_hash){
		$.extend(_Local, p_hash);
		return _Local;
	};

	iris.local.Data = function (p_label, p_value){
		if ( p_label && p_value ) {
			_Local[p_label][iris.config.Env()] = p_value; 	
		}
		else if ( p_label ) {
			return _Local[p_label][iris.config.Env()];
		}
		else  {
			return _Local;
		}
	};

})();

//////////////////////////// EVENT ///////////////////
(function(){

	// private context
	//

	// { eventName1 : [func1, func2, ...], eventName2 : ... } 
	var _Event = {};

	function _Find(p_eventName, f_func){
		var events = _Event[p_eventName];
		if ( events ) {
			for ( var f=0, F=events.length; f<F; f++ ) {
				if ( events[f] === f_func ) {
					return f;
				}
			}
		}
		return -1;
	}

	function _Subscribe(p_eventName, f_func){
		if ( !_Event[p_eventName] ) {
			_Event[p_eventName] = [];
		}

		var index = _Find( p_eventName, f_func );
		if ( index==-1 ) {
			index = _Event[p_eventName].length;
		}

		_Event[p_eventName][index] = f_func;

	};

	function _Remove(p_eventName, f_func){
		var index = _Find(p_eventName, f_func);
		if ( index!=-1 ){
			_Event[p_eventName].splice(index,1);
		}
	};

	function _Notify(p_eventName, p_data){
		if ( _Event[p_eventName] ) {
			var funcs = _Event[p_eventName];
			for ( var f=0, F=funcs.length; f<F; f++ ) {
				funcs[f](p_data);
			}
		}
	};

	// public interfaces
	//
	iris.event.Subscribe = function (p_eventName, f_func){
		_Subscribe(p_eventName, f_func);
	};

	iris.event.Remove = function (p_eventName, f_func){
		_Remove(p_eventName, f_func);
	};
	
	iris.event.Notify = function (p_eventName, p_data){
		_Notify(p_eventName, p_data);
	};

})();


//////////////////////////// NET ///////////////////
(function(){

	// private context
	//
	var _BaseUri = "";

	// public interfaces
	//
	iris.net.BaseUri = function (p_baseUri){
		if ( p_baseUri ) {
			_BaseUri = p_baseUri;
		}
		return _BaseUri;
	};

})();

//////////////////////////// LANG ///////////////////
(function(){

	// private context
	//
	var _Locale = {};
	var _Data = {};

	function _Load(p_locale, p_data){
		_Locale = p_locale;
		_Data[_Locale] = p_data;
	};

	function _Get(p_label, p_locale){
		var locale = ( p_locale ) ? p_locale : _Locale;
		var value  = _Data[locale][p_label];
		if ( !value ) value = "??" + p_label + "??";
		return value;
	};

	function _Parse(p_html){
		var html = p_html;
		var matches = html.match(/@@[A-Z_\.]+@@/g);
		if ( matches ) {
			var f, F = matches.length;
			for ( f=0; f<F; f++ ) {
				html = html.replace(matches[f], _Get(matches[f].substring(2,matches[f].length-2)));
			}
		}
		return html;
	};

	// public interfaces
	//
	iris.lang.Load = function (p_locale, p_data){
		iris.out.D("[iris.lang.Load]", p_locale, p_data);
		return _Load(p_locale, p_data);
	};

	iris.lang.LoadFrom = function (p_locale, p_uri){
		iris.out.D("[iris.lang.LoadFrom]", p_locale, p_uri);
		
		$.ajax(
			{ url: p_uri
			, dataType: "json"
			, async: false
			, cache: true
			, success : 
				function (p_data){
					_Load(p_locale, p_data);
					iris.out.D("[iris.lang.LoadFrom] loaded", p_data);
				}
			, error : 
				function (p_err) {				
					iris.out.E(p_err.status, "Error loading lang file", p_uri);
				}
			}
		);
	};

	iris.lang.Parse = function (p_html){
		return _Parse(p_html);
	};
	
	iris.lang.Get = function (p_label, p_locale) {
		return _Get(p_label, p_locale);
	};

})();

//////////////////////////// UI ///////////////////
(function(){

	// private context
	//
	function _HashToDom(p_hash, p_dom, p_filter){
		if ( p_filter ){
			var filter;
			for ( var f=0, F=p_filter.length; f<F; f++ ){
				filter = p_hash[p_filter[f]];
				if ( filter ) {
					p_dom.setAttribute(p_filter[f], filter);
				}
			}
		}
		else {
			for ( var label in p_hash){
				p_dom.setAttribute(label, p_hash[label]);
			}
		}
		return p_dom;
	}

	function _DomToHash(p_dom) {
		var hash = {};
		var attrs = p_dom.attributes;
		var label;
		for( var f=0, F=attrs.length; f<F; f++ ) {
			label = attrs[f].name;
			if ( label.indexOf("_")==0 ){
				label = label.substr(1);
			}
			hash[label] = attrs[f].value;
		}
		return hash;
	}

	function _Template(p_id){
		var $tmpl = $( iris.lang.Parse( "<div>" + document.getElementById(p_id).innerHTML + "</div>" )  );
		
		return {
			 $Get : function (p_id) {
				 if ( p_id ) {
					 return $tmpl.find("[_id=" + p_id + "]");
				 }
				 return $tmpl.children();
			}
			,To : function (p_$container) {
				p_$container.replaceWith( $tmpl.children() );
			}
			,AppendTo : function (p_$container) {
				p_$container.append( $tmpl.children() );
			}
		};
	}

	// public interfaces
	//
	iris.ui.Template = function (p_id){
		return _Template(p_id); 
	};

	iris.ui.DomToHash = function (p_dom, p_hash){
		return _DomToHash(p_dom, p_hash);
	};

	iris.ui.HashToDom = function (p_hash, p_dom, p_filter){
		return _HashToDom(p_hash, p_dom, p_filter);
	};

})();


//////////////////////////// SCREEN ///////////////////
(function(){
	
	// private context
	//
	var  _Screen = {}
		,_ScreenClass = {}
		,_Context = {}
		,_CurrentScreen = null
		,_LastScreen = {}
		,_Transition
		,_History = []
		,_HistoryCurrent = 0
	;

	var _NoneFx = {
		In : 
			function (p_screen, p_params){
				p_screen.__$__().show();
				p_screen.__OnAwake__(p_params);
			}
		, Out : 
			function (p_screen){
				p_screen.__$__().hide();
				p_screen.__OnSleep__();
			}
	};

	var _FadeFx = {
		In : function (p_screen, p_params){
			p_screen.__$__()
				.stop()
				.css("opacity", 0)
				.fadeTo(
					"fast"
					, 1
					, function (){
						p_screen.__OnAwake__(p_params);		
					}
				)
				.show();
			;
		}
		, Out : function (p_screen){
			p_screen.__$__()
				.stop()	
				.css("opacity", 1)
				.fadeTo(
					"fast"
					, 0
					, function (){
						p_screen.__$__().hide();
						p_screen.__OnSleep__();
					}
				)
			;
		}
	};

	var _HorizontalForwardFx = {
		In : function (p_screen, p_params){
			p_screen
				.__$__()
				.stop()
				.css( { "position" : "absolute", "left" : p_screen.__$__().width() } )
				.show()
				.animate(
						{ 'left': 0 }
						,'fast'
						, function (){
							p_screen.__OnAwake__(p_params);		
						}
				)
			;
		}
		, Out : function (p_screen){
			p_screen
			.__$__()
			.stop()
			.css( { "position" : "absolute", "left" : 0 } )
			.animate(
					{ 'left': -p_screen.__$__().width() }
					,'fast'
					,''
					, function () {
						p_screen.__$__().hide();
						p_screen.__OnSleep__();
					}
			);
		}
	};

	var _HorizontalBackwardFx = {
		In : function (p_screen, p_params){
			p_screen
				.__$__()
				.css( "left", -p_screen.__$__().width() )
				.show()
				.stop()
				.animate(
					{ 'left': 0 }
					,'fast'
					, function (){
						p_screen.__OnAwake__(p_params);		
					}
				)
			;
		}
		, Out : function (p_screen){
			p_screen
				.__$__()
				.stop()
				.animate(
					{ 'left': p_screen.__$__().width() }
					,'fast'
					,''
					, function () {
						p_screen.__$__().hide();
						p_screen.__OnSleep__();
					}
				)
			;
		}
	};

	_Transition = {
		NONE : {
			  Forward  : _NoneFx
			, Backward : _NoneFx
		}
		, FADE : { 
			  Forward  : _FadeFx
			, Backward : _FadeFx
		} 
		, HORIZONTAL : {
			  Forward 	: _HorizontalForwardFx
			, Backward 	: _HorizontalBackwardFx
		}
	};

	function _Goto (p_screenId, p_back, p_removeForwardHistory, p_settings){
		iris.out.D("[iris.screen.Goto]", p_screenId, p_settings);

		var settings = $.extend({}, iris.screen.transition.Settings);
		$.extend(settings, p_settings);

		if ( !_ScreenClass.hasOwnProperty(p_screenId) ) {
			iris.out.E( "[iris.screen.Goto] iris.screen.Add() has to be previosly called", p_screenId );
		}
		else {

			if ( settings["replaceInHistory"] ) {
				settings["saveInHistory"] = true;
				_HistoryCurrent--;
			}

			if ( settings["saveInHistory"] ) {
				_History[_HistoryCurrent] = { "screenId": p_screenId, "params": settings["params"], "transition": settings["transition"] };
				_HistoryCurrent++;
				if ( p_removeForwardHistory ) {
					_History = _History.slice(0, _HistoryCurrent);
				}
			}

			if ( !_Screen.hasOwnProperty(p_screenId) ) {
				_Screen[p_screenId] = new _ScreenClass[p_screenId]();
				_Context[p_screenId].append( _Screen[p_screenId].__$__().hide() );
			}

			_CurrentScreen = _Screen[p_screenId];

			var contextId = _CurrentScreen.__$__().parent().attr("_id");

			var transitionFx = p_back ? _Transition[settings["transition"]]["Backward"] : _Transition[settings["transition"]]["Forward"]; 

			if ( _LastScreen.hasOwnProperty(contextId) ) {
				transitionFx.Out(_LastScreen[contextId]);
			}
			transitionFx.In(_CurrentScreen, settings["params"]);

			_LastScreen[contextId] = _CurrentScreen;

			iris.event.Notify( iris.event.SCREEN_NAVIGATION, { "current" : _HistoryCurrent, "total" : _History.length } );
		}
		
	}
	
	function _MergeDefaultSettings (p_settings) {
		var settings = $.extend({}, iris.screen.transition.Settings);
		$.extend(settings, p_settings);
		return settings;
	}

	// events
	//
	iris.event.SCREEN_NAVIGATION = "iris_event_OnScreenNavigation"; 
	
	// public interfaces
	//
	iris.screen.transition = {
		  NONE : "NONE"
		, FADE : "FADE"
		, HORIZONTAL : "HORIZONTAL"
		, Add : function (p_transitionId, p_transition){
			_Transtion[p_transitionId] = p_transition;
		}
	};

	iris.screen.transition.Settings = {
		  "transition" 		: iris.screen.transition.NONE
		, "params"	 		: null
		, "saveInHistory" 	: true
		, "replaceInHistory": false
	};

	iris.screen.Add = function (p_$context, p_screenId, p_screenClass) {
		iris.out.D("[iris.screen.Add]", p_$context, p_screenId);
		
		_ScreenClass[p_screenId] = p_screenClass;
		_Context[p_screenId] = p_$context;
		
		if ( p_$context.get(0) === document.body ) {
			p_$context.attr("_id", "iris_body");
		}
	};
	
	iris.screen.Forward = function () {
		if ( _HistoryCurrent < _History.length ) {
iris.out.D("[iris.screen.Forward]", _HistoryCurrent, _History);

			var nextScreen = _History[_HistoryCurrent];
	
			var settings = _MergeDefaultSettings({
				  "params" : nextScreen["params"]
				, "transition" : nextScreen["transition"]  
				, "saveInHistory" : false
				, "replaceInHistory" : false
			});

			_HistoryCurrent++;
iris.out.D("[iris.screen.Forward]", nextScreen["screenId"], true, false, settings, _HistoryCurrent, _History);
			_Goto(nextScreen["screenId"], false, false, settings);			
		}
		else {
			iris.out.E("[iris.screen.Forward]", "no more elements in history");	
		}		
	};

	iris.screen.Back = function () {
iris.out.D("[iris.screen.Back]", _HistoryCurrent, _History);
		if ( _HistoryCurrent > 0 ) {
			_HistoryCurrent--;
			
			var prevScreen = _History[_HistoryCurrent-1];

			var settings = _MergeDefaultSettings({
				  "params" : prevScreen["params"]
				, "transition" : _History[_HistoryCurrent]["transition"]  
				, "saveInHistory" : false
				, "replaceInHistory" : false
			});
iris.out.D("[iris.screen.Back]", prevScreen["screenId"], true, false, settings, _HistoryCurrent, _History);
			_Goto(prevScreen["screenId"], true, false, settings);
		}
		else {
			iris.out.E("[iris.screen.Back]", "no more elements in history");	
		}
	};

	iris.screen.Goto = function (p_screenId, p_settings) {
		_Goto(p_screenId, false, true, p_settings);
	};

})();

/*****************************************
 * INITIALIZATION
 * 
 * 
 */

(function () {

	if( typeof jQuery === "undefined" ) {
		iris.out.E( "jQuery " + iris.JqMinVersion() + "+ previous load required" );
	}
	else if ( $().jquery < iris.JqMinVersion() ) {
		iris.out.E( "jQuery " + $().jquery + " currently loaded, jQuery " + bbva.front.JqMinVersion() + "+ required" );
	}

	var base = document.getElementsByTagName("base");
	if ( base.length > 0 ) {
		base = base[0].attributes["href"].value;
	}
	else {
		base = "/";
	}
	baseUri = document.location.protocol + "//" + document.location.host + base;

	iris.net.BaseUri( baseUri );

})();
